/* --COPYRIGHT--,BSD
 * Copyright (c) 2012, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --/COPYRIGHT--*/
#include "inc/hw_memmap.h"
#include "usci_i2c.h"
#include "ucs.h"
#include "wdt_a.h"
#include "gpio.h"
#include "I2C_IO_Driver.h"

//*****************************************************************************
//! This example shows how to configure the I2C module as a master for
//! multi byte transmission in interrupt driven mode. The address of the slave
//! module is set in this example.
//!
//! This example uses the following peripherals and I/O signals.  You must
//! review these and change as needed for your own board:
//! - I2C peripheral
//! - GPIO Port peripheral (for I2C pins)
//! - SCL2
//! - SDA
//!
//! This example uses the following interrupt handlers.  To use this example
//! in your own application you must add these interrupt handlers to your
//! vector table.
//! - USCI_B1_VECTOR.
//
//*****************************************************************************
//*****************************************************************************
//
//Set the address for slave module. This is a 7-bit address sent in the
//following format:
//[A6:A5:A4:A3:A2:A1:A0:RS]
//
//A zero in the "RS" position of the first byte means that the master
//transmits (sends) data to the selected slave, and a one in this position
//means that the master receives data from the slave.
//
//*****************************************************************************
#define SLAVE_ADDRESS 0x75//0x17//0xE8
//*****************************************************************************
//
//Specify number of bytes to be transmitted
//
//*****************************************************************************
#define TXLENGTH 2//0x04


unsigned char PCA_OutputMask [8];
enum ePCA_MASK {
	U7_SI_Port_0_Mask = 0,
	U7_SI_Port_1_Mask,
	U8_SO_Port_0_Mask,
	U8_SO_Port_1_Mask,
	U10_MISC_LED_Port_0_Mask,
	U10_MISC_LED_Port_1_Mask,
	U9_Port_0_Mask,
	U9_Port_1_Mask
};
void PCA_OutputMask_init(void)
{
	int i;
	for (i=0; i<8; i++)
		PCA_OutputMask[i] = 0;
}
unsigned char transmitData[40] = {
		PCA_CONFIG_PORT_0,
		0,
		PCA_CONFIG_PORT_1,
		0x0,
		PCA_OUTPUT_PORT_0,
		0x55,
		PCA_OUTPUT_PORT_1,
		0x55,
		PCA_OUTPUT_PORT_0,
		0xAA,
		PCA_OUTPUT_PORT_1,
		0xAA};

unsigned char transmitCounter = 0;
unsigned char txContinue = 0;
#define READ 1
#define WRITE 2
unsigned char txState = 0;
unsigned char rcvdData;
unsigned char slvAddr = 0;

int sendCmdData(unsigned char slaveAddr, unsigned char cmd, unsigned char mask)
{
	unsigned int failCount = 0;
	USCI_I2C_setSlaveAddress(USCI_B1_BASE, slaveAddr);
	USCI_I2C_enable(USCI_B1_BASE);
	txContinue = 1;
	txState = WRITE;
	transmitCounter = 0;

	transmitData[++transmitCounter] = mask;
	USCI_I2C_masterMultiByteSendStart(USCI_B1_BASE, cmd, 10);

	while (USCI_I2C_isBusBusy(USCI_B1_BASE))
	{
		__delay_cycles(10);
		if (failCount++ == 1000)
		{
			__no_operation();
			break;
//			failCount = 0;
//			return -1;
		}
	}
	if (failCount > 10)
	{
		__no_operation();
	}
	transmitCounter = 0;
	return 0;
//	__delay_cycles(10);

//	USCI_I2C_masterMultiByteSendNext(USCI_B1_BASE, mask);

//	__delay_cycles(10);

//	USCI_I2C_masterMultiByteSendStop(USCI_B1_BASE);

	//Delay between each transaction
//	__delay_cycles(10);
}
int readCmdData(unsigned char slaveAddr, unsigned char cmd, unsigned char * buff)
{
	unsigned int failCount = 0;
	USCI_I2C_setSlaveAddress(USCI_B1_BASE, slaveAddr);
	USCI_I2C_enable(USCI_B1_BASE);

	txState = READ;
	slvAddr = slaveAddr;
	transmitCounter = 1;
	USCI_I2C_masterMultiByteSendStart(USCI_B1_BASE, cmd, 10);
	
//	transmitData[++transmitCounter] = mask;
	
	while (USCI_I2C_isBusBusy(USCI_B1_BASE))
	{
		__delay_cycles(10);
		if (failCount++ == 1000)
		{
			__no_operation();
			break;
//			failCount = 0;
//			return -1;
		}
	}
	if (failCount > 10)
	{
		__no_operation();
	}
	transmitCounter = 0;
	return 0;

//	__delay_cycles(10);

//	USCI_I2C_masterMultiByteSendNext(USCI_B1_BASE, mask);

//	__delay_cycles(10);

//	USCI_I2C_masterMultiByteSendStop(USCI_B1_BASE);

	//Delay between each transaction
//	__delay_cycles(10);
}
void I2CioDriver_initTx(void)
{
    //Set Transmit mode
    USCI_I2C_setMode(USCI_B1_BASE, USCI_I2C_TRANSMIT_MODE);

    //Enable I2C Module to start operations
    USCI_I2C_enable(USCI_B1_BASE);

    //Enable transmit Interrupt
    USCI_I2C_enableInterrupt(USCI_B1_BASE, USCI_I2C_TRANSMIT_INTERRUPT);

    __bis_SR_register(GIE);

}
void I2CioDriver_reset(void)
{
	// not sure if this is safe!!! (for SO's)
	// I2C Reset, pin 9.5
    GPIO_setAsOutputPin(GPIO_PORT_P9, GPIO_PIN5);
    GPIO_setOutputLowOnPin(GPIO_PORT_P9, GPIO_PIN5);
    GPIO_setOutputHighOnPin(GPIO_PORT_P9, GPIO_PIN5);
}
void I2CioDriver_init(void)
{
	PCA_OutputMask_init();

	//Assign I2C pins to USCI_B1
    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5, GPIO_PIN4);
    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P3, GPIO_PIN7);

    //Initialize Master
    USCI_I2C_masterInit(USCI_B1_BASE, USCI_I2C_CLOCKSOURCE_SMCLK, UCS_getSMCLK(UCS_BASE), USCI_I2C_SET_DATA_RATE_400KBPS);

    //Specify slave address
    USCI_I2C_setSlaveAddress(USCI_B1_BASE,SLAVE_ADDRESS);

    I2CioDriver_initTx();

	// I2C Reset, pin 9.5
    GPIO_setAsOutputPin(GPIO_PORT_P9, GPIO_PIN5);
    GPIO_setOutputLowOnPin(GPIO_PORT_P9, GPIO_PIN5);
    GPIO_setOutputHighOnPin(GPIO_PORT_P9, GPIO_PIN5);
//    GPIO_toggleOutputOnPin

    sendCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, OSO_MASK);  // sets OSO's to input

    // configure: 0->output, 1->input
	sendCmdData(PCA_U7_SI,  PCA_CONFIG_PORT_0, 0);
	sendCmdData(PCA_U7_SI,  PCA_CONFIG_PORT_1, 0);
	sendCmdData(PCA_U8_SO,  PCA_CONFIG_PORT_0, 0);
	sendCmdData(PCA_U8_SO,  PCA_CONFIG_PORT_1, 0);
//	sendCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, 0);
	sendCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_1, 0);
    sendCmdData(PCA_U9, PCA_CONFIG_PORT_1, 0); // all outputs
	sendCmdData(PCA_U9, PCA_CONFIG_PORT_0, 0xFF); // all inputs
}
void I2C_SO_AllOn(void)
{
	// configure: 0->output, 1->input
	sendCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, 0);
	sendCmdData(PCA_U10_MISC_LED,  PCA_OUTPUT_PORT_0, ~OSO_MASK);
//	sendCmdData(PCA_U10_MISC_LED,  PCA_OUTPUT_PORT_0, ~OSO_3);
	sendCmdData(PCA_U8_SO,  PCA_OUTPUT_PORT_0, ~SO_LED_MASK);
}
void I2C_SO_AllOff(void)
{
	// configure as input
//	sendCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, OSO_MASK);
//	sendCmdData(PCA_U8_SO, PCA_OUTPUT_PORT_0, 0xFF);
	I2C_SO_Off(OSO_MASK, SO_LED_MASK);
}
void I2C_SO_test(void)
{
	unsigned char temp = 0;
	temp = OSO_MASK + PRIMARY;
	sendCmdData(PCA_U10_MISC_LED, PCA_OUTPUT_PORT_0, 0);
	// configure: 0->output, 1->input
	sendCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, temp);
	readCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, &rcvdData);

	temp = rcvdData & ~OSO_MASK;

	// configure: 0->output, 1->input
	sendCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, temp);
	readCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, &rcvdData);

	sendCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, 0x55);
	readCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, &rcvdData);

	sendCmdData(PCA_U10_MISC_LED, PCA_OUTPUT_PORT_0, 0); // 0 led on, 1 led off

	// get the current port configuration
	readCmdData(PCA_U10_MISC_LED,  PCA_OUTPUT_PORT_0, &rcvdData);

	temp = OSO_MASK;
	sendCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, temp);
	sendCmdData(PCA_U10_MISC_LED, PCA_OUTPUT_PORT_0, 0);

	temp = SO_LED_MASK;
	sendCmdData(PCA_U8_SO, PCA_CONFIG_PORT_0, temp);
	sendCmdData(PCA_U8_SO, PCA_OUTPUT_PORT_0, 0);

	// this does not turn off the OSO  led's
//	SO_AllOff();
//	sendCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, 0);
//	sendCmdData(PCA_U10_MISC_LED, PCA_OUTPUT_PORT_0, OSO_MASK);

}
void LED_SafeOn(unsigned char addr, unsigned char port, unsigned char mask)
{
	unsigned char configPort, outputPort, temp;
	if (port==0)
	{
		configPort = PCA_CONFIG_PORT_0;
		outputPort = PCA_OUTPUT_PORT_0;
	}
	else
	{
		configPort = PCA_CONFIG_PORT_1;
		outputPort = PCA_OUTPUT_PORT_1;
	}
	readCmdData(addr, configPort, &rcvdData);
	temp = rcvdData & ~mask;

	// configure: 0->output, 1->input
	sendCmdData(addr, configPort, temp);
	readCmdData(addr, configPort, &rcvdData);
	if (rcvdData == temp)
		__no_operation();

	readCmdData(addr, outputPort, &rcvdData);
	temp = rcvdData & ~mask;
	sendCmdData(addr, outputPort, temp);

	readCmdData(addr, outputPort, &rcvdData);
	if (rcvdData == temp)
		__no_operation();
}
void I2C_SO_On(unsigned char maskOSO, unsigned char maskSO)
{
	LED_SafeOn(PCA_U10_MISC_LED, 0, maskOSO);
	LED_SafeOn(PCA_U8_SO, 0, maskSO);
/*
	unsigned char temp = 0;
	// get the current port configuration
	readCmdData(PCA_U10_MISC_LED,  PCA_CONFIG_PORT_0, &rcvdData);
	rcvdData &= ~mask;

	// configure: 0->output, 1->input
	sendCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, rcvdData);

	// get the current port configuration
	readCmdData(PCA_U10_MISC_LED,  PCA_OUTPUT_PORT_0, &rcvdData);

	rcvdData &= ~mask;
	sendCmdData(PCA_U10_MISC_LED,  PCA_OUTPUT_PORT_0, rcvdData);
	*/
//	sendCmdData(PCA_U10_MISC_LED,  PCA_OUTPUT_PORT_0, ~OSO_3);
}
void I2C_SO_Off(unsigned char maskOSO, unsigned char maskSO)
{
	// configure as input
//	sendCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, OSO_MASK);
//	sendCmdData(PCA_U8_SO, PCA_OUTPUT_PORT_0, 0xFF);

//	unsigned char temp = 0;
	// get the current port configuration
	readCmdData(PCA_U10_MISC_LED,  PCA_CONFIG_PORT_0, &rcvdData);
	// configure as input
	// configure: 0->output, 1->input
	sendCmdData(PCA_U10_MISC_LED, PCA_CONFIG_PORT_0, rcvdData | maskOSO);

	readCmdData(PCA_U10_MISC_LED,  PCA_OUTPUT_PORT_0, &rcvdData);
	sendCmdData(PCA_U10_MISC_LED, PCA_OUTPUT_PORT_0, rcvdData | maskOSO);

	readCmdData(PCA_U8_SO,  PCA_OUTPUT_PORT_0, &rcvdData);
	// configure as input
	sendCmdData(PCA_U8_SO, PCA_OUTPUT_PORT_0, rcvdData | maskSO);
}


unsigned char I2C_SO_getSOstatus (void)
{
	readCmdData(PCA_U9,  PCA_INPUT_PORT_0, &rcvdData);
	return rcvdData;
}
//******************************************************************************
//
//This is the USCI_B1 interrupt vector service routine.
//
//******************************************************************************
#pragma vector = USCI_B1_VECTOR
__interrupt void USCI_B1_ISR (void)
{
	switch (txState)
	{
		case(READ):
		{
			switch (__even_in_range(UCB1IV,12))
			{
				case USCI_I2C_UCTXIFG:
				{
					if (transmitCounter == 1)
					{
						//Initiate stop only
						USCI_I2C_masterMultiByteSendStop(USCI_B1_BASE);

						//Clear master interrupt status
						USCI_I2C_clearInterruptFlag(USCI_B1_BASE,USCI_I2C_TRANSMIT_INTERRUPT);

						// after sending start/write and command byte, now send start/read
						transmitCounter++;
						//Set Master in receive mode
						USCI_I2C_setMode(USCI_B1_BASE,USCI_I2C_RECEIVE_MODE);

						USCI_I2C_setSlaveAddress(USCI_B1_BASE,slvAddr);

						//Enable I2C Module to start operations
						USCI_I2C_enable(USCI_B1_BASE);

						//Enable master Receive interrupt
						USCI_I2C_enableInterrupt(USCI_B1_BASE,USCI_I2C_RECEIVE_INTERRUPT);

						//Initiate command to receive a single character from Slave
						USCI_I2C_masterSingleReceiveStart(USCI_B1_BASE);
						//Enter low power mode 0 with interrupts enabled.
						//Wait until character is received.
//						__bis_SR_register(LPM0_bits + GIE);
						__bis_SR_register(GIE);
						__no_operation();
					}
					break;
				}
				case USCI_I2C_UCRXIFG:
				{
					//Grab data from data register
					rcvdData = USCI_I2C_masterSingleReceive(USCI_B1_BASE);

					//Send NAK condition.
					USCI_I2C_masterSendNACK(USCI_B1_BASE);

					//Exit low power mode 0 and disable GIE on interrupt exit
//						__bis_SR_register(LPM0_bits + GIE);
					__bis_SR_register(GIE);

					break;
				}
			}
			break;
		}
		case(WRITE):
		{
			switch (__even_in_range(UCB1IV,12))
			{
				case USCI_I2C_UCTXIFG:
				{
					//Check TX byte counter
					if (transmitCounter == txContinue){// < TXLENGTH){
						//Initiate send of character from Master to Slave
						USCI_I2C_masterMultiByteSendNext(USCI_B1_BASE,transmitData[transmitCounter]);

						//Increment TX byte counter
						transmitCounter++;
					} else   {
						//Initiate stop only
						USCI_I2C_masterMultiByteSendStop(USCI_B1_BASE);

						//Clear master interrupt status
						USCI_I2C_clearInterruptFlag(USCI_B1_BASE,USCI_I2C_TRANSMIT_INTERRUPT);

						//Exit LPM0 on interrupt return
		 //               __bic_SR_register_on_exit(LPM0_bits);
					}
					break;
				}
        	}
			break;
        }
    }
}
